// threadtest.cc 
//	Simple test case for the threads assignment.
//
//	Create two threads, and have them context switch
//	back and forth between themselves by calling Thread::Yield, 
//	to illustratethe inner workings of the thread system.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"

#ifdef CHANGED
#include "synch.h"
#include "cashregister.h"
#include "stockroom.h"
#include "clerk.h"
#include "stockboy.h"
#include "manager.h"
#include "customer.h"
#include "sharedDefinitions.h"
#endif

//----------------------------------------------------------------------
// SimpleThread
// 	Loop 5 times, yielding the CPU to another ready thread 
//	each iteration.
//
//	"which" is simply a number identifying the thread, for debugging
//	purposes.
//----------------------------------------------------------------------

void
SimpleThread(int which)
{
    int num;
    
    IntStatus old = interrupt->SetLevel(IntOff);
    {
        printf("I'm in a block of code with no interrupts!\n");
    }
    interrupt->SetLevel(old);

    for (num = 0; num < 5; num++) {
	    printf("*** thread %d looped %d times\n", which, num);
        currentThread->Yield();
    }
}

//----------------------------------------------------------------------
// ThreadTest
// 	Set up a ping-pong between two threads, by forking a thread 
//	to call SimpleThread, and then calling SimpleThread ourselves.
//----------------------------------------------------------------------

void
ThreadTest()
{
    DEBUG('t', "Entering SimpleTest");

    Thread *t = new Thread("forked thread");

    t->Fork(SimpleThread, 1);
    SimpleThread(0);
}

#ifdef CHANGED
// --------------------------------------------------
// Test Suite
// --------------------------------------------------


// --------------------------------------------------
// Test 1 - see TestSuite() for details
// --------------------------------------------------
Semaphore t1_s1("t1_s1",0);       // To make sure t1_t1 acquires the
                                  // lock before t1_t2
Semaphore t1_s2("t1_s2",0);       // To make sure t1_t2 Is waiting on the
                                  // lock before t1_t3 releases it
Semaphore t1_s3("t1_s3",0);       // To make sure t1_t1 does not release the
                                  // lock before t1_t3 tries to acquire it
Semaphore t1_done("t1_done",0);   // So that TestSuite knows when Test 1 is
                                  // done
Lock t1_l1("t1_l1");          // the lock tested in Test 1

// --------------------------------------------------
// t1_t1() -- test1 thread 1
//     This is the rightful lock owner
// --------------------------------------------------
void t1_t1() {
    t1_l1.Acquire();
    t1_s1.V();  // Allow t1_t2 to try to Acquire Lock

    printf ("%s: Acquired Lock %s, waiting for t3\n",currentThread->getName(),
        t1_l1.getName());
    t1_s3.P();
    printf ("%s: working in CS\n",currentThread->getName());
    for (int i = 0; i < 1000000; i++) ;
    printf ("%s: Releasing Lock %s\n",currentThread->getName(),
        t1_l1.getName());
    t1_l1.Release();
    t1_done.V();
}

// --------------------------------------------------
// t1_t2() -- test1 thread 2
//     This thread will wait on the held lock.
// --------------------------------------------------
void t1_t2() {

    t1_s1.P();  // Wait until t1 has the lock
    t1_s2.V();  // Let t3 try to acquire the lock

    printf("%s: trying to acquire lock %s\n",currentThread->getName(),
        t1_l1.getName());
    t1_l1.Acquire();

    printf ("%s: Acquired Lock %s, working in CS\n",currentThread->getName(),
        t1_l1.getName());
    for (int i = 0; i < 10; i++)
    ;
    printf ("%s: Releasing Lock %s\n",currentThread->getName(),
        t1_l1.getName());
    t1_l1.Release();
    t1_done.V();
}

// --------------------------------------------------
// t1_t3() -- test1 thread 3
//     This thread will try to release the lock illegally
// --------------------------------------------------
void t1_t3() {

    t1_s2.P();  // Wait until t2 is ready to try to acquire the lock

    t1_s3.V();  // Let t1 do it's stuff
    for ( int i = 0; i < 3; i++ ) {
    printf("%s: Trying to release Lock %s\n",currentThread->getName(),
           t1_l1.getName());
    t1_l1.Release();
    }
}

// --------------------------------------------------
// Test 2 - see TestSuite() for details
// --------------------------------------------------
Lock t2_l1("t2_l1");        // For mutual exclusion
Condition t2_c1("t2_c1");   // The condition variable to test
Semaphore t2_s1("t2_s1",0); // To ensure the Signal comes before the wait
Semaphore t2_done("t2_done",0);     // So that TestSuite knows when Test 2 is
                                  // done

// --------------------------------------------------
// t2_t1() -- test 2 thread 1
//     This thread will signal a variable with nothing waiting
// --------------------------------------------------
void t2_t1() {
    t2_l1.Acquire();
    printf("%s: Lock %s acquired, signalling %s\n",currentThread->getName(),
       t2_l1.getName(), t2_c1.getName());
    t2_c1.Signal(&t2_l1);
    printf("%s: Releasing Lock %s\n",currentThread->getName(),
       t2_l1.getName());
    t2_l1.Release();
    t2_s1.V();  // release t2_t2
    t2_done.V();
}

// --------------------------------------------------
// t2_t2() -- test 2 thread 2
//     This thread will wait on a pre-signalled variable
// --------------------------------------------------
void t2_t2() {
    t2_s1.P();  // Wait for t2_t1 to be done with the lock
    t2_l1.Acquire();
    printf("%s: Lock %s acquired, waiting on %s\n",currentThread->getName(),
       t2_l1.getName(), t2_c1.getName());
    t2_c1.Wait(&t2_l1);
    printf("%s: Releasing Lock %s\n",currentThread->getName(),
       t2_l1.getName());
    t2_l1.Release();
}
// --------------------------------------------------
// Test 3 - see TestSuite() for details
// --------------------------------------------------
Lock t3_l1("t3_l1");        // For mutual exclusion
Condition t3_c1("t3_c1");   // The condition variable to test
Semaphore t3_s1("t3_s1",0); // To ensure the Signal comes before the wait
Semaphore t3_done("t3_done",0); // So that TestSuite knows when Test 3 is
                                // done

// --------------------------------------------------
// t3_waiter()
//     These threads will wait on the t3_c1 condition variable.  Only
//     one t3_waiter will be released
// --------------------------------------------------
void t3_waiter() {
    t3_l1.Acquire();
    t3_s1.V();      // Let the signaller know we're ready to wait
    printf("%s: Lock %s acquired, waiting on %s\n",currentThread->getName(),
       t3_l1.getName(), t3_c1.getName());
    t3_c1.Wait(&t3_l1);
    printf("%s: freed from %s\n",currentThread->getName(), t3_c1.getName());
    t3_l1.Release();
    t3_done.V();
}


// --------------------------------------------------
// t3_signaller()
//     This threads will signal the t3_c1 condition variable.  Only
//     one t3_signaller will be released
// --------------------------------------------------
void t3_signaller() {

    // Don't signal until someone's waiting

    for ( int i = 0; i < 5 ; i++ )
    t3_s1.P();
    t3_l1.Acquire();
    printf("%s: Lock %s acquired, signalling %s\n",currentThread->getName(),
       t3_l1.getName(), t3_c1.getName());
    t3_c1.Signal(&t3_l1);
    printf("%s: Releasing %s\n",currentThread->getName(), t3_l1.getName());
    t3_l1.Release();
    t3_done.V();
}

// --------------------------------------------------
// Test 4 - see TestSuite() for details
// --------------------------------------------------
Lock t4_l1("t4_l1");        // For mutual exclusion
Condition t4_c1("t4_c1");   // The condition variable to test
Semaphore t4_s1("t4_s1",0); // To ensure the Signal comes before the wait
Semaphore t4_done("t4_done",0); // So that TestSuite knows when Test 4 is
                                // done

// --------------------------------------------------
// t4_waiter()
//     These threads will wait on the t4_c1 condition variable.  All
//     t4_waiters will be released
// --------------------------------------------------
void t4_waiter() {
    t4_l1.Acquire();
    t4_s1.V();      // Let the signaller know we're ready to wait
    printf("%s: Lock %s acquired, waiting on %s\n",currentThread->getName(),
       t4_l1.getName(), t4_c1.getName());
    t4_c1.Wait(&t4_l1);
    printf("%s: freed from %s\n",currentThread->getName(), t4_c1.getName());
    t4_l1.Release();
    t4_done.V();
}


// --------------------------------------------------
// t2_signaller()
//     This thread will broadcast to the t4_c1 condition variable.
//     All t4_waiters will be released
// --------------------------------------------------
void t4_signaller() {

    // Don't broadcast until someone's waiting

    for ( int i = 0; i < 5 ; i++ )
    t4_s1.P();
    t4_l1.Acquire();
    printf("%s: Lock %s acquired, broadcasting %s\n",currentThread->getName(),
       t4_l1.getName(), t4_c1.getName());
    t4_c1.Broadcast(&t4_l1);
    printf("%s: Releasing %s\n",currentThread->getName(), t4_l1.getName());
    t4_l1.Release();
    t4_done.V();
}
// --------------------------------------------------
// Test 5 - see TestSuite() for details
// --------------------------------------------------
Lock t5_l1("t5_l1");        // For mutual exclusion
Lock t5_l2("t5_l2");        // Second lock for the bad behavior
Condition t5_c1("t5_c1");   // The condition variable to test
Semaphore t5_s1("t5_s1",0); // To make sure t5_t2 acquires the lock after
                                // t5_t1

// --------------------------------------------------
// t5_t1() -- test 5 thread 1
//     This thread will wait on a condition under t5_l1
// --------------------------------------------------
void t5_t1() {
    t5_l1.Acquire();
    t5_s1.V();  // release t5_t2
    printf("%s: Lock %s acquired, waiting on %s\n",currentThread->getName(),
       t5_l1.getName(), t5_c1.getName());
    t5_c1.Wait(&t5_l1);
    printf("%s: Releasing Lock %s\n",currentThread->getName(),
       t5_l1.getName());
    t5_l1.Release();
}

// --------------------------------------------------
// t5_t1() -- test 5 thread 1
//     This thread will wait on a t5_c1 condition under t5_l2, which is
//     a Fatal error
// --------------------------------------------------
void t5_t2() {
    t5_s1.P();  // Wait for t5_t1 to get into the monitor
    t5_l1.Acquire();
    t5_l2.Acquire();
    printf("%s: Lock %s acquired, signalling %s\n",currentThread->getName(),
       t5_l2.getName(), t5_c1.getName());
    t5_c1.Signal(&t5_l2);
    printf("%s: Releasing Lock %s\n",currentThread->getName(),
       t5_l2.getName());
    t5_l2.Release();
    printf("%s: Releasing Lock %s\n",currentThread->getName(),
       t5_l1.getName());
    t5_l1.Release();
}

// --------------------------------------------------
// TestSuite()
//     This is the main thread of the test suite.  It runs the
//     following tests:
//
//       1.  Show that a thread trying to release a lock it does not
//       hold does not work
//
//       2.  Show that Signals are not stored -- a Signal with no
//       thread waiting is ignored
//
//       3.  Show that Signal only wakes 1 thread
//
//   4.  Show that Broadcast wakes all waiting threads
//
//       5.  Show that Signalling a thread waiting under one lock
//       while holding another is a Fatal error
//
//     Fatal errors terminate the thread in question.
// --------------------------------------------------
void TestSuite() {
    Thread *t;
    char *name;
    int i;

    // Test 1

    printf("Starting Test 1\n");

    t = new Thread("t1_t1");
    t->Fork((VoidFunctionPtr)t1_t1,0);

    t = new Thread("t1_t2");
    t->Fork((VoidFunctionPtr)t1_t2,0);

    t = new Thread("t1_t3");
    t->Fork((VoidFunctionPtr)t1_t3,0);

    // Wait for Test 1 to complete
    for (  i = 0; i < 2; i++ )
    t1_done.P();

    // Test 2

    printf("Starting Test 2.  Note that it is an error if thread t2_t2\n");
    printf("completes\n");

    t = new Thread("t2_t1");
    t->Fork((VoidFunctionPtr)t2_t1,0);
    t = new Thread("t2_t2");
    t->Fork((VoidFunctionPtr)t2_t2,0);

    // Wait for Test 2 to complete
    t2_done.P();

    // Test 3

    printf("Starting Test 3\n");

    for (  i = 0 ; i < 5 ; i++ ) {
    name = new char [20];
    sprintf(name,"t3_waiter%d",i);
    t = new Thread(name);
    t->Fork((VoidFunctionPtr)t3_waiter,0);
    }
    t = new Thread("t3_signaller");
    t->Fork((VoidFunctionPtr)t3_signaller,0);

    // Wait for Test 3 to complete
    for (  i = 0; i < 2; i++ )
    t3_done.P();

    // Test 4

    printf("Starting Test 4\n");

    for (  i = 0 ; i < 5 ; i++ ) {
    name = new char [20];
    sprintf(name,"t4_waiter%d",i);
    t = new Thread(name);
    t->Fork((VoidFunctionPtr)t4_waiter,0);
    }
    t = new Thread("t4_signaller");
    t->Fork((VoidFunctionPtr)t4_signaller,0);

    // Wait for Test 4 to complete
    for (  i = 0; i < 6; i++ )
    t4_done.P();

    // Test 5

    printf("Starting Test 5.  Note that it is an error if thread t5_t1\n");
    printf("completes\n");

    t = new Thread("t5_t1");
    t->Fork((VoidFunctionPtr)t5_t1,0);

    t = new Thread("t5_t2");
    t->Fork((VoidFunctionPtr)t5_t2,0);

}

Lock* clerkLineLock = new Lock("Clerk Line Lock");        // For mutual exclusion
Condition* clerkLineCV[5] = {
     new Condition("CV1"),
     new Condition("CV2"),
     new Condition("CV3"),
     new Condition("CV4"),
     new Condition("CV5")
};
int clerkLineLength[5];
int clerkStatus[5]; // 0 is busy (there & helping someone else), 1 is on break

class TempClass {
public:
    void runner() {
        // this is the "customer"
        int shortestLineIndex = -1;
        int shortestLineLength = 450;

        clerkLineLock->Acquire();
        printf("Customer %s got the lock.\n", currentThread->getName());
        for(int i = 0; i < 5; i++) {
            if(clerkStatus[i] == 1) {
                continue;
            }
            // clerk is not on break

            if(clerkLineLength[i] < shortestLineLength) {
                shortestLineLength = clerkLineLength[i];
                shortestLineIndex = i;
            }
        }
        printf("Customer %s will be standing in line %i \n\t It had %i people in it before me.\n",
                currentThread->getName(), shortestLineIndex, shortestLineLength);

        // have line index, get in line
        clerkLineLength[shortestLineIndex]++;
        clerkLineCV[shortestLineIndex]->Wait(clerkLineLock);

        printf("Customer %s was been woken up by a clerk, who will help me now. \n",
                currentThread->getName());
        // if we get here, we once again ahve the clerk line lock
        clerkLineLength[shortestLineIndex] --;
        clerkLineLock->Release();
    }
};


Locks* theLocks;
Store* theStore;


Lock* _customerLock = new Lock("Customer counter Lock");
int customerCounter = 0;
Lock* _stockBoyLock = new Lock("StockBoy counter Lock");
int stockBoyCounter = 0;


void CustomerThread() {
    //srand (time(NULL));
    int randomVar = rand()%5 + 1;
    bool wantsToReturnItems = (randomVar == 5);

    _customerLock->Acquire();
    int customerID = customerCounter;
    customerCounter++;
    _customerLock->Release();

    Customers* theCustomer = new Customers(customerID, wantsToReturnItems, theStore, theLocks);
    theCustomer->DoShopping();
}

Lock* _cashRegisterLock = new Lock("Cash Register Counter Lock");
int _cashRegisterCounter = 0;
void CashRegisterThread(int line) {

    _cashRegisterLock->Acquire();
    int myId = _cashRegisterCounter;
    _cashRegisterCounter ++;
    _cashRegisterLock->Release();

    CashRegister* cashRegister = new CashRegister(myId, myId, theLocks);
    cashRegister->DoWork();
}

void ManagerThread() {
    Manager* manager = new Manager(theLocks);
    manager->DoWork();
}

void StockBoyThread() {
	_stockBoyLock->Acquire();
	int stockBoyId = stockBoyCounter;
	stockBoyCounter++;
	_stockBoyLock->Release();

	StockBoy* theStockBoy = new StockBoy(stockBoyId, theStore, theLocks);
	theStockBoy->DoWork();
}

Lock* _counterLock = new Lock("Clerk counter Lock");
int clerkCounter = 0;
void ClerkThread() {
    Clerk* clerk = new Clerk(theLocks);
    _counterLock->Acquire();
    int myId = clerkCounter;
    clerkCounter ++;
    _counterLock->Release();
    clerk->GoToWork(myId);
}

void TestCase_1()
{
	Thread *t;
	char *name;
	theLocks = new Locks();
	theStore = new Store(theLocks);
	theLocks->testNo = 1;
	// Make Some Clerks
	for(int i = 0; i < MAX_CLERKS; i++) {
		name = new char[40];
		sprintf(name, "Clerk %i", i);
		t = new Thread(name);
		t->Fork((VoidFunctionPtr)ClerkThread,0);
	}
	//Make some customers
	for(int i = 0; i < MAXIMUM_OCCUPANCY; i++) {
		name = new char[40];
		sprintf(name, "Customer %i", i);
		t = new Thread(name);
		t->Fork((VoidFunctionPtr)CustomerThread,0);
	}
	// Wait for Test 1 to complete
	for (int  i = 0; i < MAXIMUM_OCCUPANCY; i++ )
		(theLocks->t1_done)->P();

	//lets check the selected lines


}

void Problem2() {
    Thread *t;
    char *name;

    theLocks = new Locks();
    theStore = new Store(theLocks);


    printf("Number of Clerks = [%i]\n", MAX_CLERKS);
    printf("Number of StockBoys = [%i]\n", MAX_STOCKBOYS);
    printf("Number of CashRegisters = [%i]\n", MAX_CLERKS);
    printf("Number of Customers = [%i]\n",MAXIMUM_OCCUPANCY);
    printf("Number of Managers = [%i]\n",1);
    printf("Number of Aisle = [%i]\n",MAX_AISLES);
    printf("  Items:\n");
    printf("    Number - Price\n");
    for (int i =0; i< MAX_ITEMS; i++)
    {
    	printf("    [%i] - $[%.2f]\n", i, theLocks->itemsInStore[i]->getPrice());
    }

    // Make Some Cash Registers
	for(int i = 0; i < MAX_CLERKS; i++) {
		name = new char[40];
		sprintf(name, "Register %i", i);
		t = new Thread(name);
		t->Fork((VoidFunctionPtr)CashRegisterThread,0);
	}

    // Make Some Clerks
    for(int i = 0; i < MAX_CLERKS; i++) {
        name = new char[40];
        sprintf(name, "Clerk %i", i);
        t = new Thread(name);
        t->Fork((VoidFunctionPtr)ClerkThread,0);
    }
    // Make Some StockBoy
	for(int i = 0; i < MAX_STOCKBOYS; i++) {
		name = new char[40];
		sprintf(name, "StockBoy %i", i);
		t = new Thread(name);
		t->Fork((VoidFunctionPtr)StockBoyThread,0);
	}

    // Make Some Customers
    for(int i = 0; i < MAXIMUM_OCCUPANCY; i++) {
        name = new char[40];
        sprintf(name, "Customer %i", i);
        t = new Thread(name);
        t->Fork((VoidFunctionPtr)CustomerThread,0);
    }
    currentThread->Yield();
    name = new char[40];
	sprintf(name, "Manager");
	t = new Thread(name);
	t->Fork((VoidFunctionPtr)ManagerThread,0);
    do
    {
    	theLocks->_occupancyLock->Acquire();
		if(theLocks->_customersInStore == 0) {
			theLocks->_occupancyLock->Release();
			return;
		} else {
			theLocks->_occupancyLock->Release();
		}
    	currentThread->Yield();
    }while(true);

}



void Problem3(){
  //Build the items
  //----------------------------
  //We dont want an item indexed at 0, since that would be the item number
  //So the first index would be a dummy item
  int itemArrayLength = MAX_ITEMS+1;

  //Seed for random variable
  //srand (time(NULL));
  
  Item*  itemArray= new Item[itemArrayLength]; //Initialize store items array
  int aisle = 0; //Starting aisle
  
  printf("\nBuilding Items for store-------\n");
  //Build the item array
  for(int i=1; i<itemArrayLength; i++){
    //Random prices between 0.01 and 100
    double price = ((double)(rand() % 100 + 1)/100) + (rand() % 100);
    //Increment the Aisle
    if((i-1) % MAX_ITEMS_PER_AISLE == 0){
      aisle ++;
    }
    itemArray[i].set(i, aisle, price, 1);
  }

  //Loop through all items
   for(int i=1; i<itemArrayLength; i++){
    printf("Item ID:%d",itemArray[i].getItemId());
    printf(" Aisle:%d",itemArray[i].getAisle());
    printf(" Price:%.2f\n",itemArray[i].getPrice());
  }

   //Build the aisles
   //-----------------------
   //We dont want an aisle indexed at 0, since that would be the aisle number
   //So the first index would be a dummy item
   int aisleArrayLength = MAX_AISLES+1;

  int index = 0;
  int itemIds[MAX_ITEMS_PER_AISLE];
  Aisle* aisleArray = new Aisle[aisleArrayLength]; //Initialize the store aisles

  printf("\nBuilding Aisle Information --------------\n");
  //Loop through all the aisles so we can set them up
  for(int i=1; i<aisleArrayLength; i++){
    //Loo through all the items to see which ones belong in this aisle
    for(int j=1; j<itemArrayLength; j++){
      //if the aisle id matches, add it to the aisle
      if(itemArray[j].getAisle() == i){
	itemIds[index] = j;
	index++;
      }
    }
    aisleArray[i].set(i,itemIds); //Set the Aisle info
    index = 0; //Reset the index
  }

  //Print out the Aisle information
  for(int i=1; i<aisleArrayLength; i++){
    aisleArray[i].printStats();	 
  }

  delete itemArray;
  delete aisleArray;
}


void TestCustomers(void)
{
	printf("Started TestCUstomer()\n");

}
#endif
